/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.tor.tribes.reportserver;

import de.tor.tribes.types.DSBuilding;
import de.tor.tribes.types.DSUnit;
import de.tor.tribes.types.ParserConfiguration;
import de.tor.tribes.types.ZebraReport;
import de.tor.tribes.types.ZebraGroupMembership;
import de.tor.tribes.types.ZebraReportAccessGrant;
import de.tor.tribes.types.ZebraUser;
import de.tor.tribes.types.ZebraGroup;
import de.tor.tribes.types.ZebraReportGroup;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 *
 * @author Torridity
 */
public class PersistenceImpl {

    private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("ReportServer_local");
    private EntityManager entityManager;

    public PersistenceImpl() {
    }

    public void close() {
        getEntityManager().close();
    }

    public EntityManager getEntityManager() {
        if (entityManager == null) {
            entityManager = emf.createEntityManager();
        } else {
            if (entityManager.getTransaction().isActive()) {
                Logger.getLogger(PersistenceImpl.class.getName()).log(Level.WARNING, "Transaction active, committing transaction");
                entityManager.getTransaction().commit();
            } else if (entityManager.getTransaction().isActive() && entityManager.getTransaction().getRollbackOnly()) {
                Logger.getLogger(PersistenceImpl.class.getName()).log(Level.WARNING, "Transaction is set to rollback. Performing rollback and commit.");
                entityManager.getTransaction().rollback();
                entityManager.close();
                entityManager = emf.createEntityManager();
            } else if (!entityManager.isOpen()) {
                try {
                    entityManager.close();
                } catch (Throwable t) {
                    Logger.getLogger(PersistenceImpl.class.getName()).log(Level.WARNING, "Failed to close EM", t);
                    entityManager = null;
                }
                entityManager = emf.createEntityManager();
            }
        }
        return entityManager;
    }

    public void persistUser(final ZebraUser pUser) {
        persist(pUser);
    }

    public void reloadUser(final ZebraUser pUser) {
        refresh(pUser);
    }

    public void persistUserGroup(final ZebraGroup pUserGroup) {
        persist(pUserGroup);
    }

    public void reloadUserGroup(final ZebraGroup pGroup) {
        refresh(pGroup);
    }

    public void removeUserGroup(final ZebraGroup pUserGroup) {
        remove(pUserGroup);
    }

    public void removeMembership(final ZebraGroupMembership pMembership) {
        remove(pMembership);
    }

    public void persistMembership(final ZebraGroupMembership pMembership) {
        persist(pMembership);
    }

    public void persistReportShare(final ZebraReportAccessGrant pShare) {
        persist(pShare);
    }

    public void persist(final Object pEntity) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.persist(pEntity);
        } finally {
            tx.commit();
        }
    }

    public void refresh(final Object pEntity) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.refresh(pEntity);
        } finally {
            tx.commit();
        }
    }

    public Object merge(final Object pEntity) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            return em.merge(pEntity);
        } finally {
            tx.commit();
        }

    }

    public void remove(final Object pEntity) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.remove(pEntity);
        } finally {
            tx.commit();
        }
    }

    public boolean contains(final Object pEntity) {
        EntityManager em = getEntityManager();
        return em.contains(pEntity);
    }

    public ParserConfiguration getParserConfiguration(String pServerId) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            Query query = em.createNativeQuery("SELECT * FROM ParserConfigurations WHERE serverId='" + pServerId + "'", ParserConfiguration.class);
            return (ParserConfiguration) query.getSingleResult();
        } catch (NoResultException nre) {
            //return default config
            ParserConfiguration config = new ParserConfiguration();
            config.setServerId(pServerId);
            return config;
        } finally {
            tx.commit();
        }
    }

    public DSBuilding getBuildingByName(String pName) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            Query query = em.createNativeQuery("SELECT * FROM DSBuildings WHERE name='" + pName + "'", DSBuilding.class);
            DSBuilding result = (DSBuilding) query.getSingleResult();
            return result;
        } catch (NoResultException nre) {
            return null;
        } finally {
            tx.commit();
        }
    }

    public List<DSBuilding> getBuildings() {
        return performQueryForList("SELECT * FROM DSBuildings", DSBuilding.class);
    }

    public <T> List<T> performQueryForList(String pQuery, Class<T> pClass) {
        return performQueryForList(pQuery, pClass, 0, -1);
    }

    public <T> List<T> performQueryForList(String pQuery, Class<T> pClass, int pFirstResult) {
        return performQueryForList(pQuery, pClass, pFirstResult, 10);
    }

    public <T> List<T> performQueryForList(String pQuery, Class<T> pClass, int pFirstResult, int pMaxResults) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        List<T> result = new LinkedList<T>();
        try {
            tx.begin();
            Query query = em.createNativeQuery(pQuery, pClass).setFirstResult(pFirstResult);
            if (pMaxResults > 0) {
                query.setMaxResults(pMaxResults);
            }
            result = (List<T>) query.getResultList();

        } catch (NoResultException nre) {
        } finally {
            tx.commit();
        }

        return result;
    }

    public <T> List<T> performQueryForList1(String pQuery, Class<T> pClass, int pFirstResult, int pMaxResults) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        List<T> result = new LinkedList<T>();
        try {
            tx.begin();
            Query query = em.createNativeQuery(pQuery, pClass).setFirstResult(pFirstResult);

            if (pMaxResults > 0) {
                query.setMaxResults(pMaxResults);
            }
            result = (List<T>) query.getResultList();
        } catch (NoResultException nre) {
        } finally {
            tx.commit();
        }

        return result;
    }

    public <T> T performQueryForSingleValue(String pQuery, Class<T> pClass) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        T result = null;
        try {
            tx.begin();
            Query query = em.createNativeQuery(pQuery, pClass);
            result = (T) query.getSingleResult();
        } catch (NoResultException nre) {
        } finally {
            tx.commit();
        }

        return result;
    }

    public <T> T performQueryForSingleValue(String pQuery) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        T result = null;
        try {
            tx.begin();
            Query query = em.createNativeQuery(pQuery);
            result = (T) query.getSingleResult();
        } catch (NoResultException nre) {
        } finally {
            tx.commit();
        }

        return result;
    }

    public List<ZebraGroupMembership> getGroupMemberships(ZebraUser pUser) {
        return performQueryForList("SELECT m.* FROM GroupMemberships m, UserGroups g  WHERE m.reportuser_userid='" + pUser.getId() + "' AND g.id=m.usergroup_id ORDER BY g.name", ZebraGroupMembership.class);
    }

    public DSBuilding getBuildingByPlainName(String pPlainName) {
        return performQueryForSingleValue("SELECT * FROM DSBuildings WHERE plainName='" + pPlainName + "'", DSBuilding.class);
    }

    public <T> T find(Long pId, Class pClass) {
        EntityManager em = getEntityManager();
        final EntityTransaction tx = em.getTransaction();
        T result = null;
        try {
            tx.begin();
            result = em.find((Class<T>) pClass, pId);
        } catch (NoResultException nre) {
        } finally {
            tx.commit();
        }
        return result;
    }

    public List<DSUnit> getUnits() {
        return performQueryForList("SELECT * FROM DSUnits", DSUnit.class);
    }

    public DSUnit getUnitByName(String pName) {
        return performQueryForSingleValue("SELECT * FROM DSUnits WHERE name='" + pName + "'", DSUnit.class);
    }

    public DSUnit getUnitByPlainName(String pPlainName) {
        return performQueryForSingleValue("SELECT * FROM DSUnits WHERE plainName='" + pPlainName + "'", DSUnit.class);
    }

    public List<ZebraReportAccessGrant> getReports(ZebraReportGroup pGroup, int pStartIdx) {
        return performQueryForList("SELECT r.* FROM ReportAccessGrants r, ReportGroupMapping m WHERE r.id=m.grant_id AND m.group_id='" + pGroup.getId() + "'", ZebraReportAccessGrant.class, pStartIdx, 10);
    }

    public BigInteger getReportCount(ZebraUser pOwner) {
        return performQueryForSingleValue("SELECT Count(*) FROM ReportAccessGrants WHERE user_userid='" + pOwner.getId() + "'");
    }

    public List<ZebraReportAccessGrant> getReports(ZebraUser pOwner, int pStartIdx) {
        return performQueryForList("SELECT * FROM ReportAccessGrants WHERE user_userid='" + pOwner.getId() + "'", ZebraReportAccessGrant.class, pStartIdx, 10);
    }

    public List<ZebraReportAccessGrant> getReportsAccessGrants(ZebraReport pReport) {
        return performQueryForList("SELECT g.* FROM ReportAccessGrants g WHERE g.report_id='" + pReport.getId() + "'", ZebraReportAccessGrant.class);
    }

    public boolean userExists(String pName, String pEmail) {
        return performQueryForSingleValue("SELECT * FROM ReportUsers WHERE name='" + pName + "' OR email='" + pEmail + "'", ZebraUser.class) != null;
    }

    public ZebraUser findUserByName(String pName) {
        return performQueryForSingleValue("SELECT * FROM ReportUsers u WHERE u.name='" + pName + "'", ZebraUser.class);
    }

    public ZebraUser getUser(String pName, String pPassword) {
        ZebraUser u = new ZebraUser();
        u.setPlainPassword(pPassword);
        return performQueryForSingleValue("SELECT * FROM ReportUsers u WHERE u.name='" + pName + "' AND u.passwordHash='" + u.getPasswordHash() + "'", ZebraUser.class);
    }

    public ZebraGroup getUserGroup(String pName) {
        return performQueryForSingleValue("SELECT * FROM UserGroups u WHERE u.name='" + pName + "'", ZebraGroup.class);
    }

    public List<ZebraReportGroup> getReportGroups(ZebraUser pUser) {
        return performQueryForList("SELECT * FROM ReportGroups WHERE owner_userid='" + pUser.getId() + "'", ZebraReportGroup.class);
    }

    public static void main(String[] args) {
//        ReportUser u = new ReportUser();
//        u.setName("Test");
//        u.setPlainPassword("123");
//        u.setEmail("test@mail.org");
//        PersistenceImpl.getSingleton().persistUser(u);
//        UserGroup group = new UserGroup();
//        group.setName("MyGroup");
//        group.addAdmin(u);
//        // group.addMember(u);
//        PersistenceImpl.getSingleton().persistUserGroup(group);
//        System.out.println(PersistenceImpl.getSingleton().getUser("Test", "123").getPasswordHash());
//        System.out.println(PersistenceImpl.getSingleton().getUserGroup("MyGroup").getMembers().length);
//        u.setEmail("bla@blubb.org");
//        group.addMember(u);
//        PersistenceImpl.getSingleton().persistUser(u);
//
//        PersistenceImpl.getSingleton().persistUserGroup(group);
//
//        System.out.println(PersistenceImpl.getSingleton().getUser("Test", "123").getMemberships().length);
//        System.out.println(PersistenceImpl.getSingleton().getUserGroup("MyGroup").getMembers().length);
    }
}
